package com.androidvizlab.bachelor.activities;

import java.util.Locale;

import com.androidvizlab.bachelor.R;
import com.androidvizlab.bachelor.Enums.SocketMessage;
import com.androidvizlab.bachelor.Sockets.*;
import com.androidvizlab.bachelor.Views.EditOptionFileView;
import com.androidvizlab.bachelor.customcomponents.fragments.CustomDialogFragment;
import com.androidvizlab.bachelor.customcomponents.listenersandadapters.CustomListener;
import com.androidvizlab.bachelor.customcomponents.listenersandadapters.CustomTextChangedListener;
import com.androidvizlab.bachelor.datamodels.OptionsFileModel;
import com.androidvizlab.bachelor.datamodels.VizlabInputData;
import com.androidvizlab.bachelor.services.VizLabDataMQTTPushService;
import com.androidvizlab.bachelor.utilities.ConnectivityDetector;
import com.androidvizlab.bachelor.utilities.NumberConverter;
import com.androidvizlab.bachelor.utilities.WakeLockManager;

import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

/**
 * Main app Activity to edit the option file and send command to start the
 * 3D Motion Capture application.
 *  
 * This Activity enable the user to set values on different field
 * on the option file. These values are then sent to the server via
 * socket connection which edit the actual option file before it starts
 * the program on (3D Motion tracking application). 
 * 
 * The Activity uses two fragments for showing a general and an advanced options.
 * @author The Hive
 */
public class EditOptionFileActivity extends FragmentActivity {
	
	//APPLICATION ID
	public static final String APP_ID = "com.androidvizlab.bachelor";
	
	//Listener and Observer(Controller)
	private CustomListener customListener = null; //a custom listener for events
	
	//Model
	private OptionsFileModel optionFileModel = null; //A model representing the options.txt
	
	//View
	private EditOptionFileView optionFileView = null;//View part of MVC pattern
	
	//TextWatcher - EditText validation
	private CustomTextChangedListener customTextChangedListener = null;
	
	//Input data
	private VizlabInputData inputData = null;
	
	//Fragments
	private FragmentManager fragmentManager = null;
	private FragmentTransaction fragmentTransaction = null;
	
	//Dialog fragment
	private CustomDialogFragment dialogfragment = null; //a fragment that create a dialog
	
	//Bundles and fragment argument
	private Bundle extraInfo = null;
	
	//SharedPreferences
	private SharedPreferences sharedPrefs = null; //default shared preference
	
	//Network and Connectivity
	private ConnectivityDetector connectivityDetector = null; //used to detect network and device connectivity
	
	//**** SOCKET SERVER CONNECTION AND BACKGROUND PROCESS ****//
	
	//Sockets: Client
	private ActivitySocketClient client = null;
	private Thread clientThread = null;
	
	//Socket connection settings
	private String SERVER_IP = "";
	private int PORTNR = 1330; //default
	
	//ASYNTASK - Background process
	private ConnectToSocketServerTask serverConnection = null;
	private DisconnectFromSocketServerTask serverCloseConnection = null;
	private SendDataToServerTask sendDataToServer = null;
	
	//Selected Triplets group of camera(use when production is selected)
	private String selectedTripCamGrps = "";
	
	/**
	 * Inherited method from Activity class which initialises and set the view for the
	 * given Activity.
	 */
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.editsettings);
		
		getSelectedTriCamGrpsFromBundles(); // get selected triplet group cameras
		
		initComponents(); //initialise views/UI components
		
		getDataFromPreference(); //Get data from stored preferences
		
		setPreferredLanguage();
	}
	
	@Override
	protected void onStart() {
		super.onStart();
		
		//NETWORK AND CONNECTION DETECTION
		detectNetworkConnectivity(); //Determines if the device is connected to a network
	
		//CONNECT TO SOCKET SERVER
		connectToServer();
		
		//REQUEST A COPY OF OPSTIONSFILE
		requestOptionsFile();
		
		//this.turnScreenOff();
	}

	@Override
	protected void onPause() {
		super.onPause();
		finish();
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		
		//DISCONNECT FROM SERVER
		disconnectFromServer();
	}

	/**
	 * Inflate the view/menu for this activity
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.optionsmenu, menu);
		return true;
	}
	
	/**
	 * Determines which item was selected in the menu
	 */
	@Override
	public boolean onOptionsItemSelected(MenuItem item){
		//Does the appropriate action when a menu is selected
		switch(item.getItemId())
		{
		case R.id.about:
			startActivity(new Intent("com.androidvizlab.bachelor.About"));
			break;
		
		case R.id.exit:
			this.finish();
			break;
		}
		return super.onOptionsItemSelected(item);
	}
	
	/**
	 * Executed when the back button is pressed
	 */
	@Override
	public void onBackPressed() {
		super.onBackPressed();
		onPause();
	}

	/**
	 * Initialise Button,EditText and radio buttons 
	 */	
	private void initComponents()
	{
		//Network and connectivity
		connectivityDetector = new ConnectivityDetector(this);
		
		//Socket client
		client = new ActivitySocketClient(); //create a new instance
		
		//** MVC **//
		
		//*** MODEL ***//
		//instantiate the model(Observable)
		optionFileModel = new OptionsFileModel(); //Model
		
		//*** CONTROLLER ***//
		//instantiate Observer, this is also the listener for event on the UI
		customListener = new CustomListener(); //Controller and Observer
		
		optionFileModel.addObserver(customListener); //add the observer to the observable
		
		customListener.setOptionFileModel(optionFileModel); //pass the reference of the model to the controller
		customListener.setEditOptionFileActivity(this); //pass the reference of the Activity to the controller
		
		//TextChangedListener
		customTextChangedListener = new CustomTextChangedListener();
		
		//Initialise fragment components
		fragmentManager = getSupportFragmentManager();
		
		//*** VIEW ***//
		optionFileView = new EditOptionFileView();
		
		optionFileView.setActivity(this); //Set the Activity to inflate Views
		
		//Fragment related
		optionFileView.setFragmentManager(fragmentManager);//Set fragment manager
		
		//SET LISTENER
		optionFileView.setOnClickListener(customListener);
		optionFileView.setOnItemClickListener(customListener);
		optionFileView.setOnCheckedChangeListener(customListener);
		optionFileView.setCustomTextChangedListener(customTextChangedListener);
		
		optionFileView.initCustomFragments(); //initialise Fragments
		optionFileView.initUIComponents(); //initialise UI 
		
		customListener.setOptionFileView(optionFileView); //Set View reference to Model
	}
	
	/**
	 * Creates and show a dialog
	 * 
	 * @param tag a tag to identify the fragment
	 * @param title the title of the dialog
	 * @param content the message on the dialog.
	 */
	public void showDialog(String tag, String title, String content)
	{
		dialogfragment = new CustomDialogFragment();
		dialogfragment.setContext(this);
		dialogfragment.setTitle(title);
		dialogfragment.setContent(content);
		dialogfragment.setOnClickListener(customListener);
		
		fragmentTransaction = fragmentManager.beginTransaction();
		
		fragmentTransaction.add(dialogfragment, tag);
		fragmentTransaction.addToBackStack(null);
		fragmentTransaction.commit();
	}
	
	//*** SHAREDPREFERENCE ***//
	
	/**
	 * This method fetch stored information in the SharedPreference
	 * associated with this application
	 */
	private void getDataFromPreference()
	{
		sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
		
		SERVER_IP = sharedPrefs.getString("serveraddress", "");
		PORTNR = NumberConverter.convertToInt(sharedPrefs.getString("portnumber", "1330"),1330);
	}
	
	//*** EXTRAS AND BUNDLES ***//
	/**
	 * Use primarily when this Activity is started from the ViewResultDataActivity and the user
	 * has selected a group of cameras on the list.
	 * 
	 * The user must also choose the Production mode to begin tracking with real data.
	 */
	public void getSelectedTriCamGrpsFromBundles()
	{
		Bundle bundle = getIntent().getExtras();
		
		if(bundle != null)
		{
			selectedTripCamGrps = bundle.getString("selected_tripcamgrps");	
			Log.i("selected trip cam grps", selectedTripCamGrps);
		}
	}
	
	//*** SOCKET CONNECTION AND DATA TRANSFER ***//
	
	/**
	 * Connects to the socket server using an AsyncTask that runs in the background.
	 */
	private void connectToServer()
	{
		if(client != null && !client.isConnected())
		{
			serverConnection = new ConnectToSocketServerTask();
			serverConnection.setClient(client); //pass the client
			serverConnection.execute();
		}
	}
	
	/**
	 * Disconnect from the socket server by closing the socket connection and stopping the client thread.
	 */
	private void disconnectFromServer()
	{
		if(client != null && client.isConnected())
		{
			serverCloseConnection = new DisconnectFromSocketServerTask();
			serverCloseConnection.execute();
		}
	}
	
	/**
	 * Send a VizlabInpuData containing the commands that the user
	 * specified. This commands will be use to edit the "options.txt" file. 
	 * @param input a VizlabInputData objet containing command specified by the user.
	 */
	public void sendDataToSocketServer(VizlabInputData input)
	{
		//Check if the client is still connected to insure that data can be transmitted
		if(!client.isConnected())
		{
			serverConnection.execute();
		}
		
		if(selectedTripCamGrps != null && !selectedTripCamGrps.equals(""))
		{
			VizlabInputData prodInput = input;
			prodInput.setSelectedTripCamGrps(selectedTripCamGrps);
			
			sendDataToServer = new SendDataToServerTask();
			sendDataToServer.execute(prodInput);
		}
		else
		{
			sendDataToServer = new SendDataToServerTask();
			sendDataToServer.execute(input);
		}
	}
	
	/**
	 * Method to send request to server
	 * @param request string command as a request
	 */
	public void sendQueryToSocketServer(String request)
	{
		//Check if the client is still connected to insure that data can be transmitted
		//if(client != null && client.isConnected())
		//{
			//Toast.makeText(this, client.isConnected()+"", Toast.LENGTH_LONG).show();
			//serverConnection.execute();
		//}
		
		sendDataToServer = new SendDataToServerTask();
		sendDataToServer.execute(request);
	}
	
	/**
	 * Send a request to the server to send over the "option.txt" file.
	 */
	public void requestOptionsFile()
	{
		//sendQueryToSocketServer(ActivitySocketClient.CLIENT_QUERY[2]);
		sendDataToServer = new SendDataToServerTask();
		sendDataToServer.execute(SocketMessage.GET_OPTIONSFILE);
	}
	
	//*** Network and connectivity ***//
	
	/**
	 * Helper method that determines if the device is
	 * connected to a network. An alert dialog will 
	 * pop up to warn the user if there is the device
	 * is not connected.
	 */
	private void detectNetworkConnectivity()
	{
		if(connectivityDetector.isConnectedToNetworkOnline())
		{
			Toast.makeText(this, "Device is online", Toast.LENGTH_LONG).show();
		}
		else
		{
			showDialog("no_network_alert","NO NETWORK CONNECTION","Please connect to a network.");
		}
	}
	
	//*** SCREEN OFF FUNCTION ***//
	
	private void turnScreenOff()
	{
		WakeLockManager.screenOff(this.getBaseContext(), "SCREEN_OFF_VIZLAB");
	}
	
	//*** SERVICE ***//
	
	/**
	 * Start the VizLabMQTTDataPushService when the app receives
	 * the message "PROCESS_STARTED".
	 * 
	 * The service subscribes to a given topic and waits for a publisher
	 * to send a message on the given topic.
	 */
	public void startDataPushService()
	{
		Intent intent = new Intent(getBaseContext(),VizLabDataMQTTPushService.class);
		
		//put a bundle containing the topic that the service will subscribe to(MQTT).
		Bundle bundle = new Bundle(); 
		bundle.putString("topic", "new_available_data");
		
		intent.putExtras(bundle); //put extra information through bundle to be passed to the service
		
		startService(intent); //starts the service 
	}
	
	
	/**
	 * Set language selected in the preference
	 * language option settings. 
	 */
	public void setPreferredLanguage()
	{
		Locale locale = null;
		Configuration config = null;
		
		SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
		StringBuilder builder = new StringBuilder();
		  
		builder.append(sharedPrefs.getString("supported_language", "ENGLISH"));
		  
		  if(builder.toString().equals("English"))
		  {
			  locale = new Locale("en");
              Locale.setDefault(locale);
              config = new Configuration();
              config.locale = locale;
              getBaseContext().getResources()
                      .updateConfiguration(
                              config,
                              getBaseContext().getResources()
                                      .getDisplayMetrics());
		  }
		  else if(builder.toString().equals("Norwegian"))
		  {
			  locale = new Locale("no");
              Locale.setDefault(locale);
              config = new Configuration();
              config.locale = locale;
              getBaseContext().getResources()
                      .updateConfiguration(
                              config,
                              getBaseContext().getResources()
                                      .getDisplayMetrics());
		  }
		  else
		  {
			  //TODO
		  }

	}

	//*** Getters and Setters ***// 
	
	public ActivitySocketClient getClient() {
		return client;
	}

	public void setClient(ActivitySocketClient client) {
		this.client = client;
	}
		
	//MODEL
	public OptionsFileModel getEditOptionFileModel() {
		return optionFileModel;
	}

	public void setEditOptionFileModel(OptionsFileModel editOptionFileModel) {
		this.optionFileModel = editOptionFileModel;
	}
	
	//********* ASYNC TASKS: SOCKET CONNECTION **********//
	
	/*
	 * This section allows the activity to avoid performing 
	 * socket connection in the UI thread
	 */
	
	/**
	 * Create an asynchronous task to perform a connection setup and
	 * attempt to connect to the socket server
	 */
	private class ConnectToSocketServerTask extends AsyncTask<Void,Integer,Void>
	{
		private ActivitySocketClient client = null;
		
		public ConnectToSocketServerTask()
		{
			
		}
		
		/**
		 * Set up all necessary component before executing the asynctask
		 */
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			
			clientThread = new Thread(); //create a new thread that will be use to run the ActivitySocketClient 
			
			client.registerCallbackHandler(customListener); //register a callback handler
			
			client.setServerIP(SERVER_IP); //set the server IP from the preference
			client.setPortNr(PORTNR); // set the port number from the preference
		}

		/**
		 * Performs the task in the background
		 * The task is to connect to the socket server.
		 */
		@Override
		protected Void doInBackground(Void... params) {
			
			clientThread = new Thread(client);
			
			client.initConnection(); //create a new socket and connect to the socket server
			
			clientThread.start();
			
			return null;
		}

		//GETTERS AND SETTERS
		
		public ActivitySocketClient getClient() {
			return client;
		}

		public void setClient(ActivitySocketClient client) {
			this.client = client;
		}		
	}
	
	/**
	 * Send data to the server via socket connection.
	 * Performs task in the background
	 *
	 */
	private class SendDataToServerTask extends AsyncTask<Object,Integer,Void>
	{
		//Progress bar
		ProgressDialog progressDialog;
		
		@Override
		protected void onPreExecute()
		{
			//Setup progress dialog
			progressDialog = new ProgressDialog(EditOptionFileActivity.this);
			progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
			progressDialog.setMax(100);
			progressDialog.show();
		}
		
		@Override
		protected Void doInBackground(Object... params) {
			
			for(int i = 0; i < 20; i++)
			{
				publishProgress(5); //update progress
				
				try 
				{
					Thread.sleep(88);
				} 
				catch (InterruptedException e) 
				{
					e.printStackTrace();
					continue;
				}
			}
			
			progressDialog.dismiss(); //dismiss progress bar
			
			client.sendData(params[0]);
			return null;
		}

		@Override
		protected void onProgressUpdate(Integer... progress) {
			progressDialog.incrementProgressBy(progress[0]);
		}
	}
	
	/**
	 * Disconnect from socket server and cleanup resources
	 */
	private class DisconnectFromSocketServerTask extends AsyncTask<Void,Void,Void>
	{
		@Override
		protected Void doInBackground(Void... params) {
			
			client.close();
			
			try 
			{
				clientThread.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			clientThread = null;
			return null;
		}
	}
}
